/**
 * 
 */
package org.vsg.stock.core.indicator.coordinate.shape;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.vsg.stock.core.indicator.kline.DegreesCoordinatePoint;

/**
 * @author Bill Vison
 *
 */
public class TrendingLineReliability implements LineReliability {
	
	private double coefficients;
	
	private Map<Double, List<DegreesCoordinatePoint>> sortCoordinatePoints;
	
	
	
	

	public double getCoefficients() {
		return coefficients;
	}


	/**
	 * provide calculate value 
	 * @param x
	 * @return
	 */
	private double calculateFormula(double x) {
		return  coefficients * x;
	}


	public void setCoefficients(double coefficients) {
		this.coefficients = coefficients;
	}



	public Map<Double, List<DegreesCoordinatePoint>> getSortCoordinatePoints() {
		return sortCoordinatePoints;
	}


	public void setSortCoordinatePoints(
			Map<Double, List<DegreesCoordinatePoint>> sortCoordinatePoints) {
		this.sortCoordinatePoints = sortCoordinatePoints;
	}

	
	public void analyze() {
		
		// ---- pick up point ---
		Set<Double> xIndex = sortCoordinatePoints.keySet();
		
		List<DegreesCoordinatePoint> closelyPoints = new Vector<DegreesCoordinatePoint>();
		
		Double xInd = null;
		List<DegreesCoordinatePoint>  assoDegreeCoorPointList = null;
		for (Iterator<Double> xIndexIter = xIndex.iterator(); xIndexIter.hasNext();) {
			xInd = xIndexIter.next();
			
			DegreesCoordinatePoint ultimateDegreeCoorPointList = sortCoordinatePoints.get( xInd ).get(0);
			DegreesCoordinatePoint entityCoorPointList = sortCoordinatePoints.get( xInd ).get(1);
			
			double yTheory = this.coefficients * xInd;
			
			double yInBottom = entityCoorPointList.getYvalue();
			double yInLow = ultimateDegreeCoorPointList.getYvalue();
			
			// --- match point ---
			double distanceOfBottom = Math.abs( yInBottom - yTheory );
			double distanceOfLow = Math.abs( yInLow - yTheory );
			
			double smaller = Math.min( distanceOfLow , distanceOfBottom);
			// --- fit the condication ---
			if (smaller < 1.5) {
				if (  smaller == distanceOfLow ) {
					closelyPoints.add(  ultimateDegreeCoorPointList );
				} else {
					closelyPoints.add( entityCoorPointList );
				}
			}
		}

		
		/**
		 * count total value 
		 */
		double valueOfTimes = validateTimes(closelyPoints);
		
		double valueOfCycle = validateCycle(closelyPoints);
		
		double valueOfDegree = validateDegree( closelyPoints );
		
		resultValue = valueOfTimes * 0.333 + valueOfCycle * 0.333 + valueOfDegree * 0.333;
		
	}
	
	private double resultValue = 0;
	
	
	/**
	 * validate point times
	 * @param closelyPoints
	 * @return
	 */
	private double validateTimes(List<DegreesCoordinatePoint> closelyPoints) {
		int counter = closelyPoints.size();
		
		if (counter > 3) {
			return 100;
		} else if ( counter < 3 && counter >1) {
			return 50;
		} else if (counter == 0 ) {
			return 0;
		}
		return 0;
	}
	
	/**
	 * validate degree defined
	 * @param closelyPoints
	 * @return
	 */
	private double validateDegree(List<DegreesCoordinatePoint> closelyPoints) {
		
		double sumDegree = 0;
		for (int i = 0 ; i < closelyPoints.size() ; i++) {
			sumDegree += closelyPoints.get(i).getDegrees();
		}
		double avgDegree = sumDegree / closelyPoints.size();
		
		if ( avgDegree > 20 && avgDegree < 50) {
			return 100;
		} else if (avgDegree <=20 || avgDegree >=50) {
			return 10;
		}
		return 0;
	}
	
	private double validateCycle(List<DegreesCoordinatePoint> closelyPoints) {
		
		DegreesCoordinatePoint lastPoint = closelyPoints.get( closelyPoints.size() - 1 );
		
		double maxDistance = lastPoint.getXvalue();
		
		double totalDistance = 0;
		for (int i = 0 ; i < closelyPoints.size() ; i++) {
			totalDistance += closelyPoints.get(i).getXvalue();
		}
		double avg = totalDistance / closelyPoints.size();
		
		// ---- max cycle ----
		double maxValueOfDistance = 0;
		if ( maxDistance > 50) {
			maxValueOfDistance = 100;
		} else if (maxDistance > 20 && maxDistance <= 50) {
			maxValueOfDistance = 50;
		}
		
		double avgValueOfDistance = 0;
		if ( avg > 50 ) {
			avgValueOfDistance = 100;
		} else if ( avgValueOfDistance > 20 && avgValueOfDistance <= 50) {
			avgValueOfDistance = 50;
		}
		
		return maxValueOfDistance * 0.5 + avgValueOfDistance * 0.5;
		
	}




	/* (non-Javadoc)
	 * @see org.vsg.stock.core.indicator.coordinate.shape.LineReliability#getValue()
	 */
	@Override
	public double getValue() {
		// TODO Auto-generated method stub
		return resultValue;
	}

}
